<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DataPresentation demo</title>

<link rel="stylesheet" type="text/css" href="../theme-remote.css" />
<style type="text/css">
body {
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
    font-size: 11px;
}

#debugPanel pre {
    font-family: "Lucida Console", "Courier New", Courier, monospace;
}

div.container {
    border: 0px solid green;
    margin: 10px;
}
</style>

<script type="text/javascript">
var djConfig = {
    isDebug: true,
    parseOnLoad: true,
    locale: "zh",
    extraLocale: ["en"],
    baseUrl: "../script/dojo-1.4.3/dojo/",
    modulePaths: {
        "demo": "../../../script/demo",
        "com": "../../../script/com",
        //"dojo.date.locale": "../../../script/dojo-1.4.3-fix/dojo.date/locale",
        //"dojo.date.stamp": "../../../script/dojo-1.4.3-fix/dojo.date/stamp",
        "dojox.charting.widget.Legend": "../../../script/dojo-1.4.3-fix/dojox.charting.widget/Legend",
        "dojox.widget.DataPresentation": "../../../script/dojo-1.4.3-fix/dojox.widget/DataPresentation"
    }
};
</script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.4.3/dojo/dojo.xd.js.uncompressed.js"></script>
<script type="text/javascript" src="../script/dojo-require.js"></script>
<script type="text/javascript">
    dojo.require("dojox.widget.DataPresentation");
</script>
<script type="text/javascript" src="../script/utils.js"></script>
<script type="text/javascript">
function debug(messages) {
    var msg = demo.utils._getDebugMsg.apply(null, arguments);
    console.debug(msg);

    var msgDiv = dojo.byId("debugPanelContent");
    msgDiv.innerHTML = msg;
}

function append(messages) {
    var msg = demo.utils._getDebugMsg.apply(null, arguments);

    var msgDiv = dojo.byId("debugPanelContent");
    msgDiv.innerHTML += "\n" + msg;
}

function show(id) {
    demo.utils.show(id);
}

function hide(id) {
    demo.utils.hide(id);
}
</script>
<style type="text/css">
@import "../css/rounded-button.css";
@import "../css/rounded-corners.css";

body {
    padding: 10px;
}

.dojoxLegendNode {
    border: 1px solid #CCCCCC;
    margin: 5px auto;
    padding: 2px;
}

.smallTextCentered {
    font-size: x-small;
    text-align: center;
}

.confidential {
    color: #AA0000;
    font-weight:  bold;
}

</style>
<script type="text/javascript">
var GlobalVariables = {
    "contextURL": null,
    "identifier": "moniker",
    "dp": null
};

function insertToArray(item, array, insertToFirst) {
    demo.utils.insertToArray(item, array, insertToFirst);
}

var smd = {
    overview: {}
};

smd.overview.NOSPLIT = "non";

// 'corporate' target status colours
smd.overview.GOAL_MISSED_IMPROVED_COLOUR = "#99BBEE";
smd.overview.GOAL_MISSED_OVER_5PCNT_COLOUR = "#CC3333";
smd.overview.GOAL_MISSED_UPTO_5PCNT_COLOUR = "#FFEE22";
smd.overview.GOAL_MET_COLOUR = "#99CD33";
smd.overview.GOAL_MISSING_COLOUR = "#999999";

smd.overview.CONTRASTING2 = [
  "#FFBF1F", // light orange
  "#CC6600"  // dark orange
];

smd.overview.CONTRASTING3 = [
  "#FFBF1F", // light orange
  "#FF9900", // orange
  "#CC6600"  // dark orange
];

// Strong contrasting colours
smd.overview.CONTRASTING_STRONG_COLOURS = [
  "#e31a1c", // red
  "#377db8", // blue
  "#4daf4a", // green
  "#984ea3", // purple
  "#ff7f00", // orange
  "#ffff33", // yellow
  "#a65628", // brown
  "#f781bf", // pink
  "#999999"  // gray
];

// Medium contrasting colours
smd.overview.CONTRASTING_MEDIUM_COLOURS = [
  "#ef6664",
  "#74a4cd",
  "#8ccd87",
  "#bb8cc3",
  "#feb452",
  "#ffff7f",
  "#c59672",
  "#faadd5",
  "#c5c5c5"
];

// Softer contrasting colours
smd.overview.CONTRASTING_SOFT_COLOURS = [
  "#fbb3ad",
  "#b2cce2",
  "#ccebc5",
  "#decbe4",
  "#fee9a5",
  "#ffffcc",
  "#e4d7bc",
  "#fddaec",
  "#f2f2f2"
];

smd.overview.GMIS_COLOURS = [
  "#C5C5C5",
  "#FF5151",
  "#FF855B",
  "#FFCC67",
  "#E6FF71",
  "#C0FF80",
  "#984EA3",
  "#BB8CC3",
  "#F781BF",
  "#FAADD5",
  "#377DB8"
];

smd.overview.getMappedValue = function(value, mapping) {
    var key = value + "";
    var mappedValue = mapping[key];
    return (mappedValue === undefined) ? value : mappedValue;
};

/**
 * Used to display null values as zeros in grid
 */
smd.overview.formatNullToZero = function(value, rowIndex) {
    var mapping = {
        "null": 0
    };

    var mappedValue = smd.overview.getMappedValue(value, mapping);
    return mappedValue;
};

/**
 * Used to display null values as "n/a" in grid.
 * @param {Object} value
 * @param {Object} rowIndex
 */
smd.overview.formatNullToNA = function(value, rowIndex) {
    var mapping = {
        "null": "n/a"
    };

    var mappedValue = smd.overview.getMappedValue(value, mapping);
    return mappedValue;
};


/**
 * Used to display descriptions from possible goal related status values in grid.
 * @param {Object} value
 * @param {Object} rowIndex
 */
smd.overview.formatGoalMissedStatus = function(value, rowIndex) {
    var mapping = {
        "null": "not applicable",
        "0": "0 - no goal defined",
        "1": "1 - meets or exceeds goal",
        "2": "2 - missed goal by up to 5%",
        "3": "3 - missed goal by more than 5%",
        "4": "4 - goal missed but actual better than last year"
    };

    var colorMapping = {
        "0": smd.overview.GOAL_MISSING_COLOUR, // gray
        "1": smd.overview.GOAL_MET_COLOUR, // green
        "2": smd.overview.GOAL_MISSED_UPTO_5PCNT_COLOUR, // yellow
        "3": smd.overview.GOAL_MISSED_OVER_5PCNT_COLOUR, // red
        "4": smd.overview.GOAL_MISSED_IMPROVED_COLOUR // blue
    };

    var color = smd.overview.getMappedValue(value, colorMapping);
    this.customStyles.push("background-color: " + color);

    var mappedValue = smd.overview.getMappedValue(value, mapping);
    return mappedValue;
};

/**
 * Used to display descriptions from possible goal related status values in grid.
 */
smd.overview.formatGoalExceededStatus = function(value, rowIndex) {
    var mapping = {
        "null": "not applicable",
        "0": "0 - no goal defined",
        "1": "1 - meets or is within goal",
        "2": "2 - exceeds goal by up to 5%",
        "3": "3 - exceeds goal by over 5%",
        "4": "4 - goal exceeded but actual better than last year"
    };

    var colorMapping = {
        "0": smd.overview.GOAL_MISSING_COLOUR, // gray
        "1": smd.overview.GOAL_MET_COLOUR, // green
        "2": smd.overview.GOAL_MISSED_UPTO_5PCNT_COLOUR, // yellow
        "3": smd.overview.GOAL_MISSED_OVER_5PCNT_COLOUR, // red
        "4": smd.overview.GOAL_MISSED_IMPROVED_COLOUR // blue
    };

    var color = smd.overview.getMappedValue(value, colorMapping);
    this.customStyles.push("background-color: " + color);

    var mappedValue = smd.overview.getMappedValue(value, mapping);
    return mappedValue;
};

/**
 * Used to display descriptions from Consumability target status code in grid.
 */
smd.overview.formatConsScoreStatus = function(value, rowIndex) {
    var mapping = {
        "null": "n/a",
        "0": "No target defined",
        "1": "Meets or exceeds target",
        "2": "Misses target by up to 1 point",
        "3": "Misses target by more than 1 point"
    };

    var colorMapping = {
        "0": smd.overview.GOAL_MISSING_COLOUR, // gray
        "1": smd.overview.GOAL_MET_COLOUR, // green
        "2": smd.overview.GOAL_MISSED_UPTO_5PCNT_COLOUR, // yellow
        "3": smd.overview.GOAL_MISSED_OVER_5PCNT_COLOUR // red
    };

    var color = smd.overview.getMappedValue(value, colorMapping);
    this.customStyles.push("background-color: " + color);

    var mappedValue = smd.overview.getMappedValue(value, mapping);
    return mappedValue;
};

/**
 * Used to style Criterion field in grid.
 */
smd.overview.formatCriterion = function(value, rowIndex, colorField) {
    var store = this.grid.store;
    var item = this.grid.getItem(rowIndex);
    if (item) {
        var color = store.getValue(item, colorField);
        if (color) {
            this.customStyles.push("background: " + color +"; ");
        }
    }

    return value;
};

/**
 * Used to style flags in ICAR grid
 */
smd.overview.formatIcarFlag = function(value, rowIndex) {
    // value mapping:
    // null    -> "n/a", smd-cell-notapplicable
    // -1      -> "n/a", smd-cell-notapplicable
    // 0       -> "",    smd-cell-pass
    // 1       -> "x",   smd-cell-fail
    var html = "";

    if (value === null || value === undefined) {
        this.customStyles.push("background: #eeeeee; color: #aaaaaa; ");
        html = "n/a";
        return html + "("+value+")";
    }

    var value = Math.floor(value);
    switch (value) {
        case -1:
            this.customStyles.push("background: #eeeeee; color: #aaaaaa; ");
            html = "n/a";
            break;
        case 0:
            html = "";
            break;
        case 1:
            this.customStyles.push("background: red; font-weight:bold; ");
            html = "X";
    }

    return html + "("+value+")";
};

/**
 * Used to style flags in ICAR grid
 */
smd.overview.formatIcarInterop = function(value, rowIndex) {
    // value mapping:
    // null    -> "n/a", smd-cell-notapplicable
    // 0       -> "",    smd-cell-pass
    // default -> "x",   smd-cell-fail
    var html = "";

    if (value === null || value === undefined) {
        this.customStyles.push("background: #eeeeee; color: #aaaaaa; ");
        html = "n/a";
        return html + "("+value+")";
    }

    var value = Math.floor(value);
    switch (value) {
        case 0: 
            html = "";
            break;
        default:
            this.customStyles.push("background: red; font-weight:bold; ");
            html = "X";
    }

    return html + "("+value+")";
};

/**
 * Used to style top 5 in ICAR grid
 */
smd.overview.formatIcarTop5 = function(value, rowIndex) {
    // pattern = "{0,choice,0<1|5<0}"
    // "0<1": if x > 0, then output = 1
    // "5<0": if x > 5, then output = 0
    // In summary, if x = [1,5], then output = 1

    // string mapping:
    // null    -> "n/a", smd-cell-notapplicable
    // [1,5]   -> "x",   smd-cell-fail
    // >5      -> "",    smd-cell-pass
    // default -> "n/a", smd-cell-notapplicable
    
    var html = "";

    if (value === null || value === undefined) {
        // null -> "n/a", smd-cell-notapplicable
        this.customStyles.push("text-align:center; background: #eeeeee; color: #aaaaaa; ");
        html = "n/a";
        return html + "("+value+")";
    }

    var value = Math.floor(value);

    if (value >= 1 && value <= 5) {
        // 1       -> "x",   smd-cell-fail
        this.customStyles.push("background: red; text-align:center; font-weight:bold; ");
        html = "X";
    } else if (value > 5) {
        // 0       -> "",    smd-cell-pass
        html = "";
    } else {
        // default -> "n/a", smd-cell-notapplicable
        this.customStyles.push("text-align:center; background: #eeeeee; color: #aaaaaa; ");
        html = "n/a";
    }

    return html + "("+value+")";
};

// constant for metrics type
var MetricsType = {
    COUNTS: "c",
    CUMULATIVE: "cc",
    NORMALIZED: "ncc",
    PERCENTAGE: "p",
    QUANTITY: "q"
};

function getChart(configuration) {

    var analysisSpecifier = makeAnalysisSpecifier(configuration.analysisSpecifier);
    GlobalVariables.analysisSpecifier = analysisSpecifier;

    var chartType = makeChartType(configuration);

    var params = {
        animate: true
    };

    var mode = null;
    if (configuration.chart && configuration.grid) {
        mode = "all";
    } else if (configuration.chart && !configuration.grid) {
        mode = "chart";
    } else if (!configuration.chart && configuration.grid) {
        mode = "grid";
    }

    var metaName = configuration.data.metadata.name;

    var domNode = null;

    switch (mode) {
        case "all":
            // fall-through
        case "chart":
            domNode = configuration.chart;
            params.type = "chart";

            // chart height
            var chartHeight = calcChartHeight(chartType, configuration);
            console.debug("chartHeight", chartHeight);
            dojo.style(domNode, "height", chartHeight + "px");
            break;
        case "grid":
            domNode = configuration.grid;
            params.type = "grid";
            // chart height
            dojo.style("chart_div", "height", "0");
            break;
    }

    doOverview(configuration.data);

    // setup reverse
    if ("reverse" in configuration) {
        params.reverse = configuration.reverse || false;
    }

    if (chartType && chartType.search(/bar/i) > -1) {
        params.reverse = true;
    }

    // setup title
    if (configuration.title) {
        params.titleNode = configuration.title;
    }

    // setup footer
    if (configuration.footer) {
        params.footerNode = configuration.footer;
    }

    // setup series
    var series = makeSeries(chartType, configuration, mode);
    params.series = series;

    // setup data
    //var data = makeData(chartType, configuration, series);
    params.data = configuration.data;

    // setup chart
    if (configuration.chart) {
        params.type = "chart";
        params.chartType = chartType;

        // setup tooltip
        params.tooltip = function(o) {
            var text = o.run.data[o.index];

            // discontinuous line will generate wrong o.index,
            // so we just use the value in y axis.
            if (o.element === "marker") {
                text = o.y;
            }

            return text;
        };

        // setup legend
        if (configuration.legend) {
            var legendWidget = dijit.byId(configuration.legend);
            if (legendWidget) {
                legendWidget.destroy(true);
            }
            params.legendNode = configuration.legend;
            params.legendHorizontal = true;
        }
    }

    // setup grid
    if (configuration.grid) {
        var gridWidget = dijit.byId(configuration.grid);
        if (gridWidget) {
            gridWidget.destroy(true);
        }
        gridWidget = new dojox.grid.DataGrid({
            //rowHeight: 20,
            "class": "abc",
            rowsPerPage: 30,
            autoHeight: 30 // true or positive integer no more than rowsPerPage (default 25)
        }, configuration.grid);
        params.gridNode = configuration.grid;
        params.gridWidget = gridWidget;
    }

    // setup theme
    var colors = makeChartColors(chartType, configuration, params.data, params.series);
    console.info("colors", colors);
    var theme = new dojox.charting.Theme({colors: colors});
    params.theme = theme;

    // metadata
    var metadata = configuration.data.metadata.description;
    formatMetadata(dojo.byId("metadata_div"), metadata);

    // render widget
    var dp = new dojox.widget.DataPresentation(domNode, params);

    return dp;
}

//////////////////////////////
dojo.ready(function() {
    dojo.byId('version').innerHTML = 'Version ' + dojo.version;

    debug('loaded');

    dojo.connect(dijit.byId("selector"), "onChange", function(value) {
        debug("metrics:", value);
        var url = "../data/chart/" + value + ".json";

        dojo.xhrGet({
            url: url,
            handleAs: "json",
            load: function(configuration) {
                console.debug(configuration);

                var dp = GlobalVariables.dp;
                if (dp) {
                    dp.destroy();
                }

                GlobalVariables.dp = getChart(configuration);
                console.debug("dp", GlobalVariables.dp);
            }
        });
    });

    dojo.connect(dijit.byId("spinner"), "onChange", function(value) {
        var width = Math.floor(value);

        dojo.style("datapresentation", "width", width + "px");
        refreshDP();
    });
});

//////////////////////////////


/**
 * Format metadata description items node = div node to contain formatted
 * metadata metadata = list of metadata description items array
 */
function formatMetadata(node, metadata) {
    // clear old metadata content
    dojo.empty(node);

    var description = dojo.create("table", { "align" : "center" });
    var tbody = dojo.create("tbody", null, description); 

    dojo.forEach(metadata, function(item) {
        if (item.name) {
            var tr = dojo.create("tr", null, tbody); 
            var td = dojo.create("td", { align: "right", innerHTML: item.name }, tr);
            dojo.addClass(td, "dojoxLegendText");
            var td2 = dojo.create("td", { innerHTML: ":" }, tr);
            dojo.addClass(td2, "dojoxLegendText");
            var td3 = dojo.create("td", { innerHTML: item.value }, tr);
            dojo.addClass(td3, "dojoxLegendText");
        }
    });

    node.appendChild(description);
}

function refreshDP() {
    var dp = GlobalVariables.dp;
    if (dp) {
        resizeChart(dp.chartWidget);
        dp.refresh();
    }
}

function resizeChart(chart, refresh) {
    if (!chart) {
        return;
    }

    if (refresh === true) {
        chart.resize();
        return;
    }

    // only set new dimensions to chart
    var box = dojo.marginBox(chart.node);
    chart.surface.setDimensions(box.w, box.h);
    //chart.coords = null;
}

function refreshChart(chart) {
    if (!chart) {
        return;
    }
    dojo.style("chart_div", "width", "100%");
    chart.resize();
    setAxisMinValue(chart, 0);
}

// set axis min value.
function setAxisMinValue(chart, minValue) {
    if (!chart) {
        return;
    }

    var min = Math.floor(minValue);
    var axisNames = ["primary", "secondary"];

    dojo.forEach(axisNames, function(axisName) {
        var axis = chart.getAxis("dependent-" + axisName);
        dojo.mixin(axis.opt, {min: min});
    });

    chart._makeDirty();
    chart.render();
}

function calcChartHeight(chartType, configuration) {
    var chartHeight = 300;

    if (chartType.search("Bar") > -1) {
        if (configuration.data.range.length > 5) {
            chartHeight = 20 * (configuration.data.range.length - 5) + 200;
        }
    }

    return chartHeight;
}

function makeChartType(configuration) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    var chartType = null;
    var data = configuration.data;
    var metaName = data.metadata.name;
    var type = configuration.type;

    if (!type) {
        return chartType;
    }

    // special cases for User-defined combinations
    if (metaName === "nmc_i") {
        var isMetricComparison = (analysisSpecifier.m1) ? true : false;

        if (isMetricComparison) {
            // metric comparison
            var matches = dojo.filter(data.metadata.columns, function(column) {
                return (column.type === "independent-label");
            });

            var rangeColumn = (matches.length > 0) ? matches[0] : null;
            if (rangeColumn) {
                if (rangeColumn.name === "consumability-attribute" || rangeColumn.name === "product-description") {
                    // product view
                    type = "clusteredbars";
                } else {
                    // trend view
                    type = "lines";
                }
            }
        } else {
            // CI 105 comparison
            type = "hybrid";
        }
    }

    // Use clusteredbars
    if (metaName.search(/cre|irb|uas|uac/) > -1) {
        type = "clusteredbars";
    }

    switch (type.toLowerCase()) {
        case 'hybrid3':
            chartType = "Hybrid";
            // add right y axis
            //chart.addAxis("y2",{vertical:true,fixLower:"major",fixUpper:"major",natural:true,min:0, leftBottom: false});
            break;
        case 'hybrid':
            chartType = "Hybrid";
            break;
        case 'clusteredcolumns':
            chartType = "ClusteredColumns";
            //chart.addAxis("y",{vertical:true,fixLower:"major",fixUpper:"major",natural:true,min:0});
            //chart.addAxis("x",{vertical:false,labels:labels,min:0,max:range.length+1,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"ClusteredColumns",gap:4});
            break;
        case 'clusteredbars':
            chartType = "ClusteredBars";
            //chart.addAxis("x",{fixLower:"major",fixUpper:"major",natural:true,min:0});
            //chart.addAxis("y",{vertical:true,labels:labels,min:0,max:range.length+1,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"ClusteredBars",gap:4});
            break;
        case 'areas':
            chartType = "Areas";
            //chart.addAxis("y",{vertical:true,fixLower:"major",fixUpper:"major",max:maxval,min:minval,majorTickStep:mts});
            //chart.addAxis("x",{vertical:false,labels:labels,min:1,max:range.length,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"Areas",lines:true,areas:true,markers:false});
            break;
        case 'hybrid2':
            //chart.addAxis("y2",{vertical:true,fixLower:"major",fixUpper:"major",natural:true,min:0, leftBottom: false});
            // intentional drop-through
            chartType = "Hybrid";
            break;
        case 'stackedcolumns':
            chartType = "StackedColumns";
            //chart.addAxis("y",{vertical:true,fixLower:"major",fixUpper:"major"});
            //chart.addAxis("x",{vertical:false,labels:labels,min:0,max:range.length+1,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"StackedColumns",gap:4});
            break;
        case 'stackedbars':
            chartType = "StackedBars";
            //chart.addAxis("x",{fixLower:"major",fixUpper:"minor",natural:true,min:0});
            //chart.addAxis("y",{vertical:true,labels:labels,min:0,max:range.length+1,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"StackedBars",gap:4});
            break;
        case 'stackedareas':
            chartType = "StackedAreas";
            //chart.addAxis("y",{vertical:true,fixLower:"major",fixUpper:"major"});
            //chart.addAxis("x",{vertical:false,labels:labels,min:1,max:range.length,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"StackedAreas",lines:true,areas:true,markers:false});
            break;
        case 'lines':
            chartType = "Lines";
            //chart.addAxis("y",{vertical:true,fixLower:"major",fixUpper:"major",max:maxval,min:minval,majorTickStep:mts});
            //chart.addAxis("x",{vertical:false,labels:labels,min:1,max:range.length,majorTickStep:1,minorTickStep:1});
            //chart.addPlot("default",{type:"Lines",markers:true});
            break;
        case 'bars':
            chartType = "Bars";
            break;
    }

    return chartType;
}

/**
 * Parse the period value from a string.
 * 
 * @param value
 *          The string to be parsed.
 * @return a Date object represents yearly and monthly,
 *         or a Integer represents GA+ month,
 *         or the input value if it is not in a period format.
 */
function parsePeriod(value) {
    var gaRegExp = /^GA(?:\+(\d+))?$/; // "GA", "GA+1", ...
    var yearRegExp = /^\d{4}$/; // e.g., "2009"
    var monthRegExp = /^\w{3} \d{4}$/; // e.g., "Jan 2009"
    var weekRegExp = /^W\/E (\d{2}) (\w{3}) '(\d{2}) #\d+$/; // e.g., "W/E 23 Nov '10 #3"
    var weekEndDateRegExp = /^(\d{2})\/(\d{2})\/(\d{2}) \d{2}:\d{2} (?:AM|PM)$/; // e.g., "23/11/10 12:00 AM"

    var str = dojo.trim(value);

    // GA
    if (gaRegExp.test(str)) {
        var results = gaRegExp.exec(str);
        var gaMonth = results[1] || 0;
        return gaMonth;
    }

    // date
    if (monthRegExp.test(str) || yearRegExp.test(str) || weekRegExp.test(str) || weekEndDateRegExp.test(str)) {
        var params = {locale: "en"};
        if (monthRegExp.test(str)) {
            params.selector = "date";
            params.datePattern = "MMM yyyy";
        } else if (yearRegExp.test(str)) {
            params.selector = "date";
            params.datePattern = "yyyy";
        } else if (weekRegExp.test(str)) {
            var results = weekRegExp.exec(str);
            var dd = results[1];
            var MMM = results[2];
            var yy = results[3];
            str = dd + "/" + MMM + "/" + yy;
            params.selector = "date";
            params.datePattern = "dd/MMM/yy";
            weekRegExp.exec();
        } else if (weekEndDateRegExp.test(str)) {
            var results = weekEndDateRegExp.exec(str);
            var dd = results[1];
            var MM = results[2];
            var yy = results[3];
            str = dd + "/" + MM + "/" + yy;
            params.selector = "date";
            params.datePattern = "dd/MM/yy";
            //params.timePattern = "hh:ss a";
        }

        var date = dojo.date.locale.parse(str, params);

        return date;
    }

    // not a period format, just return itself
    return value;
}


function stringComparator(value1, value2) {
    var str1 = value1.toLocaleLowerCase();
    var str2 = value2.toLocaleLowerCase();

    return str1.localeCompare(str2);
}

function periodComparator(value1, value2) {
    var period1 = parsePeriod(value1);
    var period2 = parsePeriod(value2);

    if ((period1 instanceof Date) && (period2 instanceof Date)) {
        return period1 - period2;
    }

    return stringComparator(value1, value2);
}

function makeSeries(chartType, configuration, mode) {
    var data = configuration.data;
    var range = configuration.range;
    var metaName = data.metadata.name;

    // get plot series
    var series = makeSeriesFromColumns(chartType, configuration, mode);

    // add range series
    if (data.range) {
        var rangeSeries = {datapoints: "range", name: range.name, charttype: "range", chart: false};

        // sorting range items into defined orders
        if (rangeSeries.name === "Period description") {
            //rangeSeries.sortername === "Period index";
            rangeSeries.gridcomparator = periodComparator;
        }

        var marketDriverColorFieldMap = {
            "uia_c": 8,  // ICAR
            "pco_n": 2,  // PMRs impacting consumability by attribute
            "pcc_n": 2,  // PMRs impacting consumability by criterion
            "uas_n": 9,  // Consumability survey score(s) by criterion
            "uac_p": 15, // Consumability survey score(s) by product
            "uar_n": 8   // Roadmap
        };

        var _getCriterionColorFormatter = function(colorField) {
            var formatter = function(value, rowIndex) {
                return smd.overview.formatCriterion.call(this, value, rowIndex, colorField);
            };

            return formatter;
        };

        // apply market driver color to range column
        if (metaName in marketDriverColorFieldMap) {
            var colorField = "data." + marketDriverColorFieldMap[metaName];
            rangeSeries.gridformatter = _getCriterionColorFormatter(colorField);
        }

        // add range series to first
        series.unshift(rangeSeries);
    }

    console.debug("series", series);
    return series;
}

function makeSeriesFromColumns(chartType, configuration, mode) {
    var data = configuration.data;
    var metaName = data.metadata.name;

    var lineSeries = [];
    var normalSeries = [];

    // prepare map: title->seriesIndex
    var seriesMap = {};
    dojo.forEach(data.series, function(series, index) {
        var legend = series.legend;
        seriesMap[legend] = index;
    });

    var _createSeries = function(record) {
        var seriesIndex = seriesMap[record.title];
        var datapoints = "series[" + seriesIndex + "].values";
        var namefield = "series[" + seriesIndex + "].legend";
        var newSeries = {
            datapoints: datapoints,
            namefield: namefield,
            gridformatter: smd.overview.formatNullToNA,
            gridstyles: "text-align: right;"
        };

        newSeries.chart = record.chart || false;
        newSeries.grid = record.grid || false;

        if ("gridformatter" in record) {
            newSeries.gridformatter = record.gridformatter;
        }

        if ("gridstyles" in record) {
            newSeries.gridstyles = record.gridstyles;
        }

        if ("gridcomparator" in record) {
            newSeries.gridcomparator = record.gridcomparator;
        }

        return newSeries;
    };

    var records = makeRecords(data, seriesMap);

    var PLOT_NORMAL = 0;
    var PLOT_LINE = 1;
    var PLOT_HYBRID_LINE = 2;
    var PLOT_HYBRID_STACKED = 3;

    var _getPlotType = function(record) {

        // chartType = "Lines"
        if (chartType === "Lines") {
            return PLOT_LINE;
        }

        // Trailer call survey satisfaction
        if (metaName === "sga_t") {
            // column.name = "survery-xxx-pct"
            if (record.field.search(/^survey-\w+?-pct$/) > -1) {
                return PLOT_HYBRID_LINE;
            }

            // column.name = "survery-xxx-count"
            if (record.field.search(/^survey-[-\w]+?-count$/) > -1) {
                return PLOT_HYBRID_STACKED;
            }
        }

        // nci_i & nmc_i
        if (metaName === "nci_i" || metaName === "nmc_i") {
            if (record.type === "general-ratio") {
                return PLOT_HYBRID_LINE;
            }
        }

        // OCI
        if (metaName === "ado_t" || metaName === "aoc_t"  || metaName === "poc_t" || metaName === "ioc_t") {
            if (record.field.search(/inventory/) > -1) {
                return PLOT_HYBRID_LINE;
            }
        }

        return PLOT_NORMAL;
    };

    dojo.forEach(records, function(record) {
        if (record.chart === true || record.grid === true || record.grid === null) {
            var newSeries = _createSeries(record);

            var plotType = _getPlotType(record);

            if (plotType === PLOT_LINE) {
                newSeries.charttype = "line";
            }

            if (plotType === PLOT_HYBRID_LINE) {
                newSeries.charttype = "line";
                //newSeries.axis = "secondary";
            }

            if (plotType === PLOT_HYBRID_STACKED) {
                newSeries.charttype = "stackedcolumns";
            }

            // add to series
            if (newSeries.charttype === "line") {
                lineSeries.push(newSeries);
            } else {
                normalSeries.push(newSeries);
            }
        }
    });

    var _containsChartSeries = function(seriesArray) {
        var result = dojo.some(seriesArray, function(seriesItem) {
            return seriesItem.chart === true;
        });

        return result;
    };

    var isHybridChart = _containsChartSeries(lineSeries) && _containsChartSeries(normalSeries);
    if (isHybridChart) {
        dojo.forEach(lineSeries, function(lineSeriesItem) {
            lineSeriesItem.axis = "secondary";
        });
    }

    var series = [].concat(lineSeries, normalSeries);

    return series;
}

/**
 * Records with chart/grid display flag.
 * @param {Object} data
 * @param {Object} seriesMap
 */
function makeRecords(data, seriesMap) {
    var metaName = data.metadata.name;

    // prepare
    var records = dojo.map(data.metadata.columns, function(column) {
        var record = {
            title: column.title,
            type: column.type,
            field: column.name,
            columnType: column["column-type"]
        };

        return record;
    });

    // process
    processRecords(records, metaName, seriesMap);

    return records;
}

function processRecords(records, metaName, seriesMap) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // first of all, process DIMENSION series. They will not be displayed in the chart.
    var matches = dojo.filter(records, function(record) {
        return record.columnType === "DIMENSION";
    });

    dojo.forEach(matches, function(record) {
        record.chart = false;

        if (record.field === "period-index") {
            // "Period index" series will not show in the grid,
            // but it will be used to sort the range column.
            record.grid = null;
        } else if (record.field === "ibm-week-number") {
            // "IBM week number" series will show in the grid.
            record.grid = true;
        }
    });

    // CI 105 Lite
    if (metaName === "nci_i") {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "MEASURE" || record.columnType === "COLLATERAL";
        });

        var recordFieldMap = {
            "ucc": "unique-customers",
            "cc": "pmrs-opened-cumulative",
            "ncc": "pmrs-opened-normalized-cumulative",
            "oacc": "apars-opened-cumulative",
            "oancc": "apars-opened-normalized-cumulative"
        };

        var pattern = null;
        if (analysisSpecifier.m) {
            var metrics = analysisSpecifier.m.split(",");
            var fields = dojo.map(metrics, function(metric) {
                return recordFieldMap[metric] + "$" || "";
            });
            pattern = fields.join("|");
        }

        dojo.forEach(matches, function(record) {
            record.grid = true;

            if (pattern && record.field.search(pattern) > -1) {
                record.chart = true;
            }
        });

        return;
    }

    // Classified PMR trend
    if (metaName === "psf_t") {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "MEASURE" || record.columnType === "COLLATERAL";
        });

        // normalized cumulative counts
        if (getMetricsType(analysisSpecifier) === MetricsType.NORMALIZED) {
            matches = dojo.filter(records, function(record) {
                return record.title.search("(normalized)") > -1 || record.title.search("Classified") > -1;
            });

            dojo.forEach(matches, function(record) {
                record.grid = true;

                if (record.type === "general-ratio") {
                    record.chart = true;
                }
            });

            return;
        }
    }

    // PMRs impacting consumability
    if (metaName === "pco_n" || metaName === "pcc_n") {
        dojo.forEach(records, function(record) {
            if (record.field === "consumability-market-driver-color") {
                // This series will not be displayed in the grid, but it will be used to format the grid cell.
                record.grid = null; // Use null to indicate that we will add this series but not to display it.
            }
        });
    }

    // Top customer drivers
    if (metaName === "ptd_n") {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "COLLATERAL";
        });

        // grid only
        dojo.forEach(matches, function(record) {
            record.chart = false;
            record.grid = true;
            record.gridstyles = "text-align: left;";
        });

        return;
    }

    // Consumability survey score(s) by criterion
    if (metaName === "uas_n") {
        var matches = dojo.filter(records, function(record) {
            if (record.field === "consumability-market-driver-color") {
                return true;
            }

            if (record.columnType === "MEASURE" || record.columnType === "COLLATERAL") {
                if (record.type === "set" || record.type === "sequence" || record.type === "collateral") {
                    return false;
                }

                if (record.field.search(/target|delta/) > -1) {
                    return false;
                }

                return true;
            }

            return false;
        });

        dojo.forEach(matches, function(record) {
            record.chart = false;
            record.grid = true;

            if (record.field === "consumability-market-driver-color") {
                // This series will not be displayed in the grid, but it will be used to format the grid cell.
                record.grid = null; // Use null to indicate that we will add this series but not to display it.
            }

            if (record.type === "quantity") {
                record.chart = true;
            }
        });

        return;
    }

    // Consumability survey score(s) by product
    if (metaName === "uac_p") {
        var matches = dojo.filter(records, function(record) {
            if (record.field === "consumability-market-driver-color") {
                return true;
            }

            if (record.columnType === "MEASURE" || record.columnType === "COLLATERAL") {
                if (record.type === "collateral") {
                    return false;
                }

                return true;
            }

            return false;
        });

        dojo.forEach(matches, function(record) {
            record.chart = false;
            record.grid = true;

            if (record.field === "consumability-market-driver-color") {
                // This series will not be displayed in the grid, but it will be used to format the grid cell.
                record.grid = null; // Use null to indicate that we will add this series but not to display it.
            }

            if (record.type === "quantity") {
                if (record.field.search(/target|delta/) === -1) {
                    record.chart = true;
                }
            }

            // format consumability score status
            if (record.type === "set") {
                record.gridformatter = smd.overview.formatConsScoreStatus;
                record.gridstyles = "text-align: left;";
            }
        });

        return;
    }

    // Roadmap
    if (metaName === "uar_n") {
        var matches = dojo.filter(records, function(record) {
            if (record.field === "consumability-market-driver-color") {
                return true;
            }

            if (record.field === "user-priorities" || record.field === "consumability-market-driver") {
                return false;
            }

            if (record.columnType === "MEASURE" || record.columnType === "COLLATERAL") {
                if (record.type === "sequence") {
                    return false;
                }

                return true;
            }

            return false;
        });

        dojo.forEach(matches, function(record) {
            record.chart = false;
            record.grid = true;

            if (record.field === "consumability-market-driver-color") {
                // This series will not be displayed in the grid, but it will be used to format the grid cell.
                record.grid = null; // Use null to indicate that we will add this series but not to display it.
            }

            if (record.type === "collateral") {
                record.gridstyles = "text-align: left;";
            }
        });

        return;
    }

    // ICAR
    if (metaName === "uia_c") {
        var matches = dojo.filter(records, function(record) {
            if (record.field === "consumability-market-driver-color") {
                return true;
            }

            if (record.columnType === "COLLATERAL") {
                if (record.type === "sequence" || record.type === "collateral") {
                    return false;
                }

                return true;
            }

            return false;
        });

        dojo.forEach(matches, function(record) {
            record.chart = false;
            record.grid = true;

            if (record.field === "consumability-market-driver-color") {
                // This series will not be displayed in the grid, but it will be used to format the grid cell.
                record.grid = null; // Use null to indicate that we will add this series but not to display it.
            } else if (record.field === "flag-high-pmrs") {
                record.gridformatter = smd.overview.formatIcarTop5;
            } else if (record.field === "flag-high-pmrs-in-stack") {
                record.gridformatter = smd.overview.formatIcarInterop;
            } else {
                record.gridformatter = smd.overview.formatIcarFlag;
            }
        });

        return;
    }

    // Trailer call survey satisfaction
    if (metaName === "sga_t") {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "MEASURE";
        });

        var recordFieldMap = {
            "nsi": "survey-nsi-pct",
            "vsat": "survey-vsat-pct",
            "sat": "survey-sat-pct",
            "ltsat": "survey-lsat-pct",
            "dissat": "survey-dsat-pct",
            "rcnts": "count"
        };

        var showInChartMap = {};
        if (analysisSpecifier.m) {
            // nsi,vsat,sat,ltsat,dissat,rcnts
            var metrics = analysisSpecifier.m.split(",");
            dojo.forEach(metrics, function(metric) {
                var field = recordFieldMap[metric];
                showInChartMap[field] = true;
            });
        }

        dojo.forEach(matches, function(record) {
            record.grid = true;

            // determin whether to show counts in chart
            if (record.field.search("count") > -1 && record.field !== "survey-total-count") {
                record.chart = showInChartMap["count"] || false;
            }

            // determin whether to show percentage in chart
            if (record.field.search("pct") > -1) {
                record.chart = showInChartMap[record.field] || false;
            }
        });

        return;
    }

    // Situations
    if (metaName.search(/ioc|irb/) > -1) {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "MEASURE";
        });

        dojo.forEach(matches, function(record) {
            record.grid = true;

            // only show total columns in chart

            // "Number of xxx" as the total column
            if (record.title.search(/^Number of/i) > -1) {
                record.chart = true;
            }

            // "Situation inventory" as the total column
            if (record.title.search("Situation inventory") > -1) {
                record.chart = true;
            }

            // "Total xxx" as the total column
            if (record.title.search(/total/i) > -1) {
                record.chart = true;
            }
        });

        return;
    }

    // User-defined combinations
    if (metaName === "nmc_i") {
        var matches = dojo.filter(records, function(record) {
            return record.columnType === "MEASURE" || record.columnType === "COLLATERAL";
        });

        dojo.forEach(matches, function(record) {
            record.chart = true;
            record.grid = true;
        });

        return;
    }

    // show columns
    var matches = dojo.filter(records, function(record) {
        if (record.title in seriesMap) {
            if (record.columnType === "MEASURE" || record.columnType === "COLLATERAL") {
                if (record.type !== "collateral" && record.type !== "sequence") {
                    return true;
                }
            }
        }

        return false;
    });

    // grid
    dojo.forEach(matches, function(record) {
        record.grid = true;
        if (record.field === "ibm-week-end-date") {
            record.gridcomparator = periodComparator;
        }
    });

    // counts
    processCounts(matches);

    // percentage
    processPercentage(matches);

    // quantity
    processQuantity(matches);

    // cumulative
    processCumulativeCounts(matches);

    // normalized cumulative counts
    processNormalizedCumulativeCounts(matches);

    // total
    processTotal(matches);

    // goal status
    processGoalStatus(matches);
}

function getMetricsType(analysisSpecifier) {
    var metaName = analysisSpecifier.a;

    // special case for Calls
    if (metaName.search(/cre|cfe|cbe/) > -1) {
        return MetricsType.PERCENTAGE;
    }

    // special case for APAR validity
    if (metaName.search("acv") > -1) {
        return MetricsType.PERCENTAGE;
    }

    // special case for eSupport PMR receipts & backlog
    if (metaName.search("pes") > -1) {
        return MetricsType.PERCENTAGE;
    }

    // special case for PMR first time fix
    if (metaName.search("pft") > -1) {
        return MetricsType.PERCENTAGE;
    }

    // special case for PMR FE self-sufficiency
    if (metaName.search("pss") > -1) {
        return MetricsType.PERCENTAGE;
    }

    // special case for PMR weighted backlog
    if (metaName.search("pwb") > -1) {
        return MetricsType.QUANTITY;
    }

    // special case for Solution given days
    if (metaName.search("psg") > -1) {
        return MetricsType.QUANTITY;
    }

    // special case for Time to APAR
    if (metaName.search("pta") > -1) {
        return MetricsType.QUANTITY;
    }

    // special case for Hours per problem
    if (metaName.search("php") > -1) {
        return MetricsType.QUANTITY;
    }

    // counts
    if (!analysisSpecifier.m) {
        return MetricsType.COUNTS;
    }

    // percentage
    if (analysisSpecifier.m.search(/p$/) > -1) {
        return MetricsType.PERCENTAGE;
    }

    // normalized cumulative counts
    if (analysisSpecifier.m.search(/ncc$/) > -1) {
        return MetricsType.NORMALIZED;
    }

    // cumulative counts
    if (analysisSpecifier.m.search(/cc$/) > -1) {
        return MetricsType.CUMULATIVE;
    }

    // counts
    return MetricsType.COUNTS;
}

function processCounts(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // counts
    if (getMetricsType(analysisSpecifier) === MetricsType.COUNTS) {
        console.info("c");
        dojo.forEach(records, function(record) {
            if (record.type === "count") {
                record.chart = true;
            }
        });
    }
}

function processPercentage(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // percentage
    if (getMetricsType(analysisSpecifier) === MetricsType.PERCENTAGE) {
        console.info("p");
        dojo.forEach(records, function(record) {
            if (record.type === "subset-percentage") {
                record.chart = true;
            }
        });
    }
}

function processQuantity(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // quantity
    if (getMetricsType(analysisSpecifier) === MetricsType.QUANTITY) {
        console.info("q");
        dojo.forEach(records, function(record) {
            if (record.type === "quantity") 
                record.chart = true;
        });
    }
}

function processCumulativeCounts(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // cumulative counts
    if (getMetricsType(analysisSpecifier) === MetricsType.CUMULATIVE) {
        console.info("cc");
        dojo.forEach(records, function(record) {
            if (record.columnType === "COLLATERAL" && record.title.search(/cumulative/i) > -1) {
                record.chart = true;
            }
        });
    }
}

function processNormalizedCumulativeCounts(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    // normalized cumulative counts
    if (getMetricsType(analysisSpecifier) === MetricsType.NORMALIZED) {
        dojo.forEach(records, function(record) {
            if (record.type === "general-ratio") {
                record.chart = true;
            }
        });
    }
}

function processTotal(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    var _isFilterTotal = function(analysisSpecifier) {
        var metaName = analysisSpecifier.a;

        // special case for TFSA
        if (metaName.search("afs") > -1) {
            return true;
        }

        // special case for APAR fix defects
        if (metaName.search("afd") > -1 && getMetricsType(analysisSpecifier) === MetricsType.COUNTS) {
            return true;
        }

        // special case for Hours per problem
        if (metaName.search("php") > -1) {
            return true;
        }

        // special case for PMR receipts & backlog
        if (metaName.search("prb") > -1) {
            return true;
        }

        // special case for PMR OPC classification
        if (metaName.search("pcl") > -1) {
            return true;
        }

        // no split by
        if (analysisSpecifier.sb === smd.overview.NOSPLIT) 
            return false;

        // split by
        if (analysisSpecifier.sb && analysisSpecifier.sb !== smd.overview.NOSPLIT) 
            return true;

        return false;
    };

    // not display total columns in chart
    if (_isFilterTotal(analysisSpecifier)) {
        console.info("total");

        var metaName = analysisSpecifier.a;

        var matches = dojo.filter(records, function(record) {
            return record.chart === true;
        });

        // not display total columns in chart
        dojo.forEach(matches, function(record) {

            // special case for TFSA and PMR receipts & backlog
            if (metaName.search(/afs|prb/) > -1) {
                // "xxx target" and "xxx goal" as the goal column
                if (record.type === "count" && record.field.search(/target|goal/) > -1) {
                    record.chart = false;
                }

                return;
            }

            // special case for PMR OPC classification
            if (metaName.search("pcl") > -1) {
                // "xxx Closed" as the total column
                if (record.field.search(/^pmrs-closed/) > -1) {
                    record.chart = false;
                }

                return;
            }

            // "Number of xxx" as the total column
            if (record.title.search(/^Number of/i) > -1) {
                record.chart = false;
            }

            // "Total xxx" as the total column
            if (record.title.search(/total/i) > -1) {
                record.chart = false;
            }

            // "Normalized cumulative" as the total column
            if (record.title === "Normalized cumulative") {
                record.chart = false;
            }
        });
    }
}

function processGoalStatus(records) {
    var analysisSpecifier = GlobalVariables.analysisSpecifier;

    var GoalType = {
        MISSED: "missed",
        EXCEEDED: "exceed",
        CONS_SCORE: "cons"
    };

    var _getGoalType = function(analysisSpecifier) {
        var metaName = analysisSpecifier.a;

        // special case for Call response time met
        if (metaName.search("cre") > -1) {
            return GoalType.MISSED;
        }

        // special case for TFSA
        if (metaName.search("afs") > -1) {
            return GoalType.EXCEEDED;
        }

        // special case for PMR receipts & backlog
        if (metaName.search(/prb|pes/) > -1) {
            return GoalType.EXCEEDED;
        }

        // special case for Consumability survey score(s)
        if (metaName.search("uac") > -1) {
            return GoalType.CONS_SCORE;
        }

        return null;
    };

    // format goal status description in grid
    if (_getGoalType(analysisSpecifier)) {
        console.info("goal");

        var formatter = null;
        var goalType = _getGoalType(analysisSpecifier);
        if (goalType === GoalType.MISSED) {
            formatter = smd.overview.formatGoalMissedStatus;
        } else if (goalType === GoalType.EXCEEDED) {
            formatter = smd.overview.formatGoalExceededStatus;
        } else if (goalType === GoalType.CONS_SCORE) {
            formatter = smd.overview.formatConsScoreStatus;
        }

        var matches = dojo.filter(records, function(record) {
            return record.grid === true && record.type === "set";
        });

        dojo.forEach(matches, function(record) {
            if (formatter) {
                record.gridformatter = formatter;
                record.gridstyles = "text-align: left;";
            }
        });
    }
}

function makeAnalysisSpecifier(analysisSpecifier) {
    var settings = {};
    var regExp = /[?&](\w+?)=([^&]+)/ig;

    var matches = null;
    while ((matches = regExp.exec(analysisSpecifier)) !== null) {
        // matches[0] -> str
        // matches[1] -> p1
        // matches[2] -> p2
        var key = matches[1];
        var value = matches[2];
        settings[key] = value;
    }

    return settings;
}

function makeData(chartType, configuration, seriesArray) {
    var _needToProcess = function(chartType, metaName) {
        if (chartType.search(/stacked|bars|columns|hybrid/i) > -1) { // (chartType.search(/column|hybrid/ig) > -1)
            return true;
        }

        // Trailer call survey satisfaction
        if (metaName === "sga_t") {
            return true;
        }

        return false;
    };

    var data = configuration.data;
    var metaName = data.metadata.name;

    // special cases for ICAR
    if (metaName === "uia_c")
      return data;

    // populate the series to convert null values
    var seriesIndexes = [];
    var seriesIndexesToProcess = [];

    dojo.forEach(seriesArray, function(series) {
        var seriesIndex = getSeriesIndex(series);
        if (seriesIndex < 0) {
            return;
        }

        seriesIndexes.push(seriesIndex);

        if (series.chart !== false) {

            // convert null values for line plots
            if (series.charttype === "line") {
                // FIXME: CI 105/metric comparisons still have problems on null values in line plots,
                // so we convert values here. But CI 105 Lite works fine.
                if (metaName === "nmc_i") {
                    seriesIndexesToProcess.push(seriesIndex);
                }
            }

            // convert null values for column plots
            if (_needToProcess(chartType, metaName)) {
                if (series.charttype !== "line") {
                    seriesIndexesToProcess.push(seriesIndex);
                }
            }
        }
    });

    // hacks to get around null values causing problems in the chart
    dojo.forEach(seriesIndexesToProcess, function(seriesIndex) {
        var values = [];
        if (data.series[seriesIndex]) {
            values = data.series[seriesIndex].values;
        }

        dojo.forEach(values, function(value, index, values) {
            if (value === null) {
                values[index] = 0;
            }
        });
    });

    return data;
}

function makeChartColors(chartType, configuration, data, seriesDefs) {
    var colors = [];

    var metaName = data.metadata.name;

    if (metaName.search(/cre|irb|uas|uac/) > -1) {
        colors = dojo.clone(smd.overview.CONTRASTING2);
        return colors;
    }

    if (metaName === "nmc_i" || metaName === "nci_i") {
        // Hybrid chart, need to re-create colors
        var colors_strong = dojo.clone(smd.overview.CONTRASTING_STRONG_COLOURS);
        var colors_medium = dojo.clone(smd.overview.CONTRASTING_MEDIUM_COLOURS);
        var colors_soft = dojo.clone(smd.overview.CONTRASTING_SOFT_COLOURS);

        // prepare column map first
        var columnMap = {}; // title->column
        dojo.forEach(data.metadata.columns, function(column) {
            var title = column.title;
            columnMap[title] = column;
        });

        var lineColors = [];
        var plotColors = [];
        dojo.forEach(seriesDefs, function(seriesDef) {
            // skip if not in chart
            if (seriesDef.chart === false) {
                return;
            }

            var seriesIndex = getSeriesIndex(seriesDef);
            if (seriesIndex < 0) {
                return;
            }

            var color = null;

            // Normalized cumulative, as line
            if (seriesDef.charttype === "line") {
                // strong color
                color = colors_strong.shift();
                lineColors.push(color);
                return;
            }

            var columnTitle = data.series[seriesIndex].legend;
            var column = columnMap[columnTitle];

            // Cumulative counts
            if (column["column-type"] === "COLLATERAL") {
                // medium color
                color = colors_medium.shift();
                plotColors.push(color);
                return;
            }

            // Number of unique customers
            if (column["column-type"] === "MEASURE") {
                // soft color
                color = colors_soft.shift();
                plotColors.push(color);
                return;
            }
        });

        if (chartType.search("Bar") > -1) {
            colors = [].concat(plotColors.reverse(), lineColors.reverse());
        } else {
            colors = [].concat(plotColors, lineColors.reverse());
        }
        return colors;
    }

    colors = configuration.fillColors || [];
    return colors;
}

function getSeriesIndex(series) {
    var seriesIndex = -1;

    var datapoints = series.datapoints;
    var results = datapoints.match(/^series\[(\d+)\].values$/);
    if (dojo.isArray(results) && results.length === 2) {
        seriesIndex = results[1];
    }

    return seriesIndex;
}

function makeseries(configuration, mode) {
    var lineSeries = [];
    var normalSeries = [];

    var data = configuration.data;
    var metaName = data.metadata.name;

    var seriesMap = {}; // legend/title->series
    dojo.forEach(data.series, function(series, index) {
        var legend = series.legend;
        seriesMap[legend] = index;
    });

    dojo.forEach(data.metadata.columns, function(column) {
        var columnType = column["column-type"];
        var name = column["name"];
        var title = column["title"];
        var type = column["type"];

        var newSeries = null;
        if (title in seriesMap) {
            var seriesIndex = seriesMap[title];
            var datapoints = "series[" + seriesIndex + "].values";
            var namefield = "series[" + seriesIndex + "].legend";
            newSeries = {
                datapoints: datapoints,
                namefield: namefield,
                gridstyles: "text-align: right;"
            };
        }

        if (newSeries) {
            var targetArray = normalSeries;

            // Special case for Classified PMR cumulative trend by customer activity normalized by number of unique customers.
            // There are so many columns in this table we need to trim off a bunch for the widget.
            if (metaName === "psf_t") {
                if (title.search("(normalized)") === -1 && title.search("Classified") === -1) {
                    return;
                } else {
                    newSeries.chart = false;
                }
            }

            if (columnType === "COLLATERAL") {

                // special cases for psf_t
                if (metaName === "psf_t") {
                    if (type === "general-ratio") {
                        newSeries.chart = true;
                        insertToArray(newSeries, targetArray);
                    }
                    return;
                }

                // special cases for nci_i
                if (metaName === "nci_i") {
                    if (type === "general-ratio") {
                        newSeries.charttype = "line";
                        newSeries.axis = "secondary";
                        targetArray = lineSeries;
                    } else if (type === "subset-percentage") {
                        newSeries.chart = false;
                    }
                    insertToArray(newSeries, targetArray);
                    return;
                }

                // special cases for ICAR
                if (metaName === "uia_c") {
                    if (type === "sequence" || name.search(/^consumability-market-driver/) > -1) {
                        newSeries.grid = false;
                    }

                    if (name.search(/^flag-high-pmrs$/) > -1) {
                        newSeries.gridformatter = smd.overview.formatIcarTop5;
                    } else if (name.search(/^flag-high-pmrs-in-stack$/) > -1) {
                        newSeries.gridformatter = smd.overview.formatIcarInterop;
                    } else {
                        newSeries.gridformatter = smd.overview.formatIcarFlag;
                    }
                }

                // special cases for OPC
                if (title !== "Sev 3&4" && metaName.search(/^pco/) === -1 && metaName.search(/^pcc/) === -1) {
                    insertToArray(newSeries, targetArray);
                }
            }

            if (columnType === "MEASURE") {
                // special cases for ICAR
                if (metaName === "uia_c") {
                    return;
                }

                // special cases for apars backlog and receipts
                switch (type) {
                    case "subset-percentage":
                        newSeries.type = "subset-percentage";
                        break;
                    case "count":
                        if (title === "Backlog goal" || title === "Receipts goal") {
                            newSeries.chart = false;
                        }
                        newSeries.type = "count";
                        break;
                    case "general-ratio":
                        // fall-through
                    case "set":
                        newSeries.chart = false;
                        break;
                }

                // sga_t
                if (name === "survey-total-count") {
                    newSeries.chart = false;
                } else if (name.search("critical") > -1) {
                    // critical only for grid
                    newSeries.chart = false;
                } else {

                    // NOTE: modify DataPresentation.js to set chart type for hybird stacked column plot.
                    // if (charttype === "Hybrid-StackedColumns") {
                    //     args.type = "StackedColumns";
                    // }
                    if (name.search(/^survey-[-\w]+?-count$/) > -1) {
                        newSeries.charttype = "stackedcolumns";
                    }

                    // NOTE: modify DataPresentation.js to set markers for hybird line plot.
                    // if (charttype === "Hybrid-Lines") {
                    //     args.markers = true;
                    // }
                    if (name.search(/^survey-\w+?-pct$/) > -1) {
                        newSeries.charttype = "line";
                        newSeries.axis = "secondary"; // secondary axis on the right side
                        targetArray = lineSeries;
                    }

                    if (name.search(/inventory/) > -1) {
                        newSeries.charttype = "line";
                        newSeries.axis = "secondary"; // secondary axis on the right side
                        targetArray = lineSeries;
                    }
                }

                insertToArray(newSeries, targetArray);
            }
        }
    });


    var series = [].concat(lineSeries, normalSeries);

    // post-process
    var hasPercentageColumns = dojo.some(series, function(seriesItem){
        return (seriesItem.type === "subset-percentage"); 
    });
    console.debug("subset-percentage", hasPercentageColumns);

    dojo.forEach(series, function(seriesItem) {
        // remove type attribute
        if ("type" in seriesItem) {
            switch (seriesItem.type) {
                case "count":
                    if (hasPercentageColumns) {
                        seriesItem.chart = false;
                    }
                    break;
            }

            delete seriesItem["type"];
        }

        // mode
        if (mode === "grid") {
            seriesItem.chart = false;
        }

        // add grid formatter
        if (seriesItem.grid !== false) {
            if (!("gridformatter" in seriesItem)) {
                seriesItem.gridformatter = smd.overview.formatNullToZero;
            }
        }
    });

    // add range series to first
    var rangeSeries = null;
    if (data.range) {
        var rangeName = configuration.range.name;
        rangeSeries = {datapoints: "range", name: rangeName, type: "range", chart: false};
        if (metaName === "uia_c") {
            rangeSeries.gridformatter = smd.overview.formatIcarCriteria;
        }

        insertToArray(rangeSeries, series, true);
    }

    console.debug("series", series);
    return series;
}

function doOverview(data) {
    var chartappend = true;
    var gridappend = true;
    var series = [];
    var colours = [];
    var colourscheme;
    var range = "";
    var hasSplitBy = false;
    var CCcheck = false;
    var isNormalizedCumulativeCounts = false;
    var percentagepass = true;
    var IIWcheck = false;
    var productView = false; // check if the bookmarked view is a product view
    var mcomp = false; // specical case for metric comparison charts
    var datalength = data.metadata.columns.length;
    var percentagetype = new Array(0);
    var quantitytype = new Array(0);
    var cumulativecounts = new Array(0);
    var regcounts = new Array(0);
    var griddisplay = new Array(0);
    var ncumulativecounts = new Array(0);
    
    
    for (i = 0; i < datalength; i++) {
        var column = data.metadata.columns[i];
        if (column.type == "independent-label") {
            range = column.title;
            if (column.name == "product-description" || column.name == "consumability-attribute") 
                productView = true;
        }

        if ((column.type == "quantity") && (column.name != 'average-response-time')) 
            // special case for call response time met
            quantitytype.push(column.title);
        
        if ((column.type == "subset-percentage") && percentagepass) 
            percentagetype.push(column.title);
        
        // special cases for OPC
        if (percentagepass && (column["column-type"] == "COLLATERAL") && (column.title != "Sev 3&4") && (data.metadata.name.match("^pco") != "pco") && (data.metadata.name.match("^pcc") != "pcc")) 
            cumulativecounts.push(column.title);
        
        // special cases for apars backlog and receipts
        if (column.type == "count" && column.title != "Backlog goal" && column.title != "Receipts goal" && column.title != "Backlog Index") {
            if (CCcheck) {
                // special case for cumulative counts
                if ((column["column-type"] == "COLLATERAL") && (column.title != "Sev 3&4")) {
                    regcounts.push(column.title);
                }
            } else {
                regcounts.push(column.title);
            }
        }
        if ((column.type == "general-ratio") || (column.type == "set") || (column.type == "count")) {
            griddisplay.push(column.title);
            if ((column.type == "general-ratio") && isNormalizedCumulativeCounts) 
                ncumulativecounts.push(column.title);
        }
    }
}
</script>
</head>
<body class="tundra">

  <h2 id="version">Unknown Version</h2>

  <div id="main" class="container">

    <div id="titlePanel" dojoType="dijit.TitlePane" title="Panel" style="">
      <div id="resizablePanel" style="position:relative; padding: 10px; border:1px solid black;">

        <!-- Content -->
        <div class="content">
          <div id="datapresentation" style="width: 100%;">
            <!-- title -->
            <div style="text-align: center;">
              <h2 id="title_div" style="margin-bottom: 0;"></h2>
            </div>

            <!-- chart -->
            <div id="chart_div"></div>

            <!-- legend -->
            <div id="legend_div"></div>

            <!-- grid -->
            <div id="grid_div"></div>

            <!-- metadata -->
            <div id="metadata_div"></div>

            <!-- footer -->
            <div style="text-align: center;">
              <div id="footer_div" class="smallTextCentered confidential"></div>
            </div>
          </div>
        </div>

        <!-- Resize Handle -->
        <div id="resizeHandle" dojoType="dojox.layout.ResizeHandle" targetId="resizablePanel"></div>
      </div>
    </div>

    <!-- Button Panel -->
    <div dojoType="dojo.dnd.Moveable">
      <div id="buttonPanel" dojoType="dijit.TitlePane" title="Button Panel" style="">
        <select id="selector" dojoType="dijit.form.FilteringSelect">
            <option value="cre_t">Call response time met trend</option>
            <option value="cre_t-sep">Call response time met trend - Show eSupport problems separately</option>
            <option value="cre_p">Call response time met by product</option>
            <option value="cre_p-sep">Call response time met by product - Show eSupport problems separately</option>
            <option value="cbe_t">Call back end technical update timeliness trend</option>
            <option value="aoc_t">APAR OCI trend</option>
            <option value="aop_t-c">APAR opened trend - count</option>
            <option value="aop_t-c-sev">APAR opened trend - count, severity</option>
            <option value="aop_t-cc">APAR opened trend - cumulative</option>
            <option value="aop_t-cc-sev">APAR opened trend - cumulative, severity</option>
            <option value="aop_t-ncc">APAR opened trend - normalized</option>
            <option value="aop_t-ncc-sev">APAR opened trend - normalized, severity</option>
            <option value="acc_t-per">APAR closed trend - percentage</option>
            <option value="acc_p-per-cc">APAR closed product - percentage, cc</option>
            <option value="abl_t-sev">APAR inventory trend - severity</option>
            <option value="afs_t">TFSA receipts & backlog trend</option>
            <option value="ado_t">APAR fix defects OCI trend</option>
            <option value="poc_t">PMR OCI trend</option>
            <option value="pop_t">PMRs opened trend</option>
            <option value="pop_t-sev">PMRs opened trend by severity</option>
            <option value="pbl_t-age">PMR backlog trend by age</option>
            <option value="prb_t">PMR receipts & backlog trend</option>
            <option value="pes_t">eSupport PMR receipts & backlog percentage trend</option>
            <option value="nci_i-cc,ncc">CI 105 lite - PMR Receipts with PMRs per unique customer</option>
            <option value="nci_i-ucc,ncc">CI 105 lite - Unique customer counts with PMRs per unique customer</option>
            <option value="nci_i-oacc,oancc">CI 105 lite - APAR (TFSA) receipts with APARs (TFSAs) per unique customer</option>
            <option value="nci_i-ucc,oancc">CI 105 lite - Unique customer counts with APARs (TFSAs) per unique customer</option>
            <option value="nci_i-cc">CI 105 lite - PMR Receipts</option>
            <option value="nci_i-oacc">CI 105 lite - APAR (TFSA) receipts</option>
            <option value="nci_i-ucc">CI 105 lite - Unique customer counts</option>
            <option value="nci_i-ncc">CI 105 lite - PMRs per unique customer</option>
            <option value="nci_i-oancc">CI 105 lite - APARs (TFSAs) per unique customer</option>
            <option value="pcl_t">PMR OPC classification trend, count</option>
            <option value="pcl_t-cc">PMR OPC classification trend, cumulative</option>
            <option value="pcl_t-per">PMR OPC classification trend, percentage</option>
            <option value="pcl_t-ncc">PMR OPC classification trend, normalized</option>
            <option value="psf_t">Classified PMR trend by customer activity, count</option>
            <option value="psf_t-cc">Classified PMR trend by customer activity, cumulative</option>
            <option value="psf_t-per">Classified PMR trend by customer activity, percentage</option>
            <option value="psf_t-ncc">Classified PMR trend by customer activity, normalized</option>
            <option value="psf_n">Classified PMRs by customer activity and situation, count</option>
            <option value="psf_n-per">Classified PMRs by customer activity and situation, percentage</option>
            <option value="uas_n">Consumability survey score(s) by criterion</option>
            <option value="uac_p">Consumability survey score(s) by product</option>
            <option value="uar_n">Roadmap</option>
            <option value="uia_c">ICAR</option>
            <option value="nmc_i">CI 105 comparision - raw</option>
            <option value="nmc_i-n_r">CI 105 comparision - normalized, raw</option>
            <option value="nmc_i-n_r_u">CI 105 comparision - normalized, raw, unique</option>
            <option value="nmc_i-trunk">CI 105 comparision - trunk</option>
            <option value="nmc_i-m-p">Metric comparision - product</option>
            <option value="nmc_i-m-t">Metric comparision - trend</option>
            <option value="pcc_n">PMRs impacting consumability by criterion</option>
            <option value="ptd_n">Top customer drivers (issues)</option>
            <option value="sga_t">Trailer call survey satisfaction trend</option>
            <option value="ioc_t">Situation OCI trend</option>
            <option value="irb_t">Situation receipts & backlog trend</option>
            <option value="irb_p">Situation receipts & backlog by product</option>
            <option value="xot_t">OneTeam Scorecard</option>
        </select>
        <input id="spinner" dojoType="dijit.form.NumberSpinner" name="minval" value="1000" smallDelta="100" constraints="{min:0,max:10000,places:0}" intermediateChanges="true" />
        <div dojoType="dijit.form.Button" onClick="refreshDP();">Refresh</div>
      </div>
    </div>
  </div>

  <!-- Debug Panel -->
  <div id="debugPanel" dojoType="dojox.layout.FloatingPane" title="Console" resizable="true" dockable="true" style="position: absolute; bottom: 20px; right: 20px; width: 500px; height: 150px;">
    <pre id="debugPanelContent"></pre>
  </div>
</body>
</html>
